#include "ZeroServer.h"

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

ZeroServer::ZeroServer()
{
	mClientMaxNum = 10;
	mClient = new zClient*[mClientMaxNum];
	mClientNum = 0;
	pthread_mutex_init(&mClientMutex, NULL);
}

ZeroServer::~ZeroServer()
{
	for (int i = 0;i < mClientNum;i++)
		if (mClient[i])
			delete mClient[i];
	if (mClient)
		delete [] mClient;
}

bool ZeroServer::startServer()
{
	int ret;
	ret = pthread_create(&mServerTid, NULL, ZeroServer::Run, this);
	if (ret) {
		std::cout << "Error: create server thread: "
			<< std::strerror(ret) << std::endl;
		return false;
	}
	return true;
}

void* ZeroServer::ServeClient(void *data)
{
	zClient *client = (zClient*)(((void**)data)[0]);
	ZeroServer *me = (ZeroServer*)(((void**)data)[1]);
	close(client->fd);
	me->delClient(client);
	return (void*)0;
}

void ZeroServer::addClient(zClient *client)
{
	pthread_mutex_lock(&mClientMutex);
	if (mClientNum == mClientMaxNum) {
		zClient **oldList = mClient;
		mClientMaxNum += 10;
		mClient = new zClient*[mClientMaxNum];
		for (int i = 0;i < mClientNum;i++)
			mClient[i] = oldList[i];
		delete [] oldList;
	}
	mClient[mClientNum++] = client;
	pthread_mutex_unlock(&mClientMutex);
}

void ZeroServer::delClient(zClient *client)
{
	pthread_mutex_lock(&mClientMutex);
	for (int i = 0;i < mClientNum;i++)
		if (mClient[i] == client) {
			delete mClient[i];
			mClient[i] = mClient[--mClientNum];
			break;
		}
	pthread_mutex_unlock(&mClientMutex);
}

int ZeroServer::getClientNum()
{
	return mClientNum;
}

void* ZeroServer::Run(void* data)
{
	int serverFd, clientFd;
	int ret;
	socklen_t addrLen;
	struct sockaddr_in serverAddr, clientAddr;
	ZeroServer *me = (ZeroServer*)data;
	void* ptrs[2];
	ptrs[1] = (void*)me;
	zClient *client;
	serverFd = socket(AF_INET, SOCK_STREAM, 0);
	if (serverFd == -1) {
		std::cout << "[E] ZeroServer: open socket..." << std::endl;
		pthread_exit((void*)1);
	}
	memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serverAddr.sin_port = htons(1231);
	if (bind(serverFd, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
		std::cout << "[E] ZeroServer: bind socket..." << std::endl;
		pthread_exit((void*)2);
	}
	if (listen(serverFd, 5) == -1) {
		std::cout << "[E] ZeroServer: listen socket..." << std::endl;
		pthread_exit((void*)3);
	}
	while (true) {
		addrLen = sizeof(sockaddr_in);
		clientFd = accept(serverFd, (sockaddr*)&clientAddr, &addrLen);
		if (clientFd < 0) {
			std::cout << "[E] ZeroServer: accept..." << std::endl;
			pthread_exit((void*)4);
		}
		std::cout << "[I] ZeroServer: got connected..." << std::endl;
		std::cout << "[I] ZeroServer: clientNum:" << me->getClientNum()
			<< std::endl;
		client = new zClient;
		client->fd = clientFd;
		client->addr = ntohl(clientAddr.sin_addr.s_addr);
		ptrs[0] = (void*)client;
		ret = pthread_create(&client->tid, NULL, ServeClient, (void*)ptrs);
		if (ret) {
			std::cout << "[E] ZeroServer: create serve thread: "
				<< std::strerror(ret) << std::endl;
			close(clientFd);
			delete client;
		}
		me->addClient(client);
	}
	close(serverFd);
	return (void*)0;
}
